package com.greatwall.hip.cms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.greatwall.hip.cms.constant.AlertLevelEnum;
import com.greatwall.hip.cms.constant.Constant;
import com.greatwall.hip.cms.constant.SymptomCategoryEnum;
import com.greatwall.hip.cms.dao.*;
import com.greatwall.hip.cms.entity.*;
import com.greatwall.hip.cms.model.Req;
import com.greatwall.hip.cms.model.Result;
import com.greatwall.hip.cms.service.EpidemicMonitorService;
import com.greatwall.hip.cms.util.AlertLevelUtil;
import com.greatwall.hip.cms.vo.EpidemicMonitorVO;
import com.greatwall.hip.cms.vo.EpidemicMonitorVO.MonMonitor;
import com.greatwall.hip.cms.vo.MonitorDiseaseAndPacsBarVO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * (LyDiseaseTag)表服务实现类
 *
 * @author makejava
 * @since 2021-07-06 19:42:50
 */
@Service
public class EpidemicMonitorServiceImpl implements EpidemicMonitorService {

    @Resource
    private LyDiseaseTagDao lyDiseaseTagDao;
    @Resource
    private LyDiseaseSymptomDao lyDiseaseSymptomDao;

    @Resource
    private LyPacsReportDao lyPacsReportDao;

    @Resource
    private LyZyYbqkDao lyZyYbqkDao;

    @Resource
    private LyMzYbqkDao lzMzYbqkDao;

    @Value("${monitor.epidemic.low}")
    private int low;

    @Value("${monitor.epidemic.middle}")
    private int middle;

    @Value("${monitor.epidemic.high}")
    private int high;

    /**
     * 1.提示
     * 2.低风险
     * 3.中风险
     * 4.高风险
     *
     * @param percent
     * @return
     */
    private AlertLevelEnum alertLevel(double percent) {
        if (percent <= low) {
            return AlertLevelEnum.NORMAL;
        } else if (percent <= middle) {
            return AlertLevelEnum.LOW;
        } else if (percent <= high) {
            return AlertLevelEnum.MIDDLE;
        } else {
            return AlertLevelEnum.HIGH;
        }
    }

    @Override
    public MonitorDiseaseAndPacsBarVO diseaseAndPacsBar(Req req) {
        MonitorDiseaseAndPacsBarVO monitorVO = new MonitorDiseaseAndPacsBarVO();
        //查询住院人数
        LyZyYbqk lyZyYbqk = lyZyYbqkDao.selectBasicInfo(req);
        if (lyZyYbqk != null) {
            monitorVO.setInpatientCount(lyZyYbqk.getPatientCount());
        }

        //查询门诊人数
        LyMzYbqk lyMzYbqk = lzMzYbqkDao.selectBasicInfo(req);
        if (lyMzYbqk != null) {
            monitorVO.setOutpatientCount(lyMzYbqk.getPatientCount());
        }

        Integer diseaseCount = lyDiseaseTagDao.queryCount(req.getStartDate(), req.getEndDate());

        //查询门急诊疾病
        monitorVO.setDiseaseTag(queryDisease(req, diseaseCount == null ? BigDecimal.ZERO.intValue() : diseaseCount, monitorVO));
        monitorVO.setSymptom(querySymptomPercent(req, diseaseCount == null ? BigDecimal.ZERO.intValue() : diseaseCount));

        queryAbnormalAndChild(req, monitorVO);
        return monitorVO;
    }

    @Override
    public Result<EpidemicMonitorVO> lastMonMonitor(Req req) {
        EpidemicMonitorVO monitorVO = new EpidemicMonitorVO();
        HashMap<String, List<MonMonitor>> radarMonitor = queryLastMonMonitor(req);
        monitorVO.setRadarMonitor(radarMonitor);

        /**
         * 风险计算逻辑：
         * <p>
         * 四类指标的风险组合决定了总体风险等级：
         * a)高等风险的情况：四类指标都是高风险；
         * b)中等风险的情况：存在高风险(除全高外)，或全为中风险；
         * c)低等风险的情况：存在中风险(除全中外)，或全为低风险；
         * d)无风险情况：其他组合；
         * e)上述总体风险等级判断的组合支持可配
         */
        List<MonMonitor> calendarMonitor = Lists.newArrayList();
        radarMonitor.keySet().forEach(date -> {
            List<MonMonitor> monMonitors = radarMonitor.get(date);

            //计算警告等级
            List<AlertLevelEnum> alertList = monMonitors.stream().map(monMonitor ->
                    AlertLevelEnum.getByLevel(monMonitor.getLevel())).collect(Collectors.toList());

            calendarMonitor.add(MonMonitor.builder().date(date).level(AlertLevelUtil.calAlertLevel(alertList).getLevel()).build());
        });

        monitorVO.setCalendarMonitor(calendarMonitor.stream()
                .sorted(Comparator.comparing(MonMonitor::getDate)).collect(Collectors.toList()));

        return Result.success(monitorVO);
    }

    /**
     * LastMonMonitor
     *
     * @return
     */
    private HashMap<String, List<MonMonitor>> queryLastMonMonitor(Req req) {
        //上个月日期
        DateTime lastMonDate = DateUtil.offsetDay(DateUtil.parse(req.getEndDate(), DatePattern.PURE_DATE_PATTERN), -29);

        DateTime offsetDay = DateUtil.offsetDay(lastMonDate, -7);
        String offsetDayStr = offsetDay.toString(DatePattern.PURE_DATE_PATTERN);

        HashMap<String, List<MonMonitor>> lastMonMonitorMap = Maps.newHashMap();


        Map<String, List<LyDiseaseTag>> diseaseLastMonthData = lyDiseaseTagDao.queryDiseaseByBetweenDate(offsetDayStr, req.getEndDate())
                .stream().collect(Collectors.groupingBy(LyDiseaseTag::getDay));

        Map<String, List<LyDiseaseSymptom>> symptomLastMonthData = lyDiseaseSymptomDao.querySymptomByDate(offsetDayStr, req.getEndDate())
                .stream().collect(Collectors.groupingBy(LyDiseaseSymptom::getDay));

        Map<String, List<LyPacsReport>> pacsReports = lyPacsReportDao.queryAllByDate(offsetDayStr, req.getEndDate())
                .stream().collect(Collectors.groupingBy(LyPacsReport::getDay));

        Map<String, List<LyPacsReport>> childPacsReports = lyPacsReportDao.queryChildAllByDate(offsetDayStr, req.getEndDate())
                .stream().collect(Collectors.groupingBy(LyPacsReport::getDay));

        //查询最近30-7天
        new DateRange(offsetDay, DateUtil.parse(req.getEndDate(), DatePattern.PURE_DATE_PATTERN), DateField.DAY_OF_MONTH).forEach(new Consumer<DateTime>() {
            @Override
            public void accept(DateTime dateTime) {
                String date = dateTime.toString(DatePattern.PURE_DATE_PATTERN);
                //初始化，如果没有当天数据，默认填充空集合，防止后面查询异常
                diseaseLastMonthData.computeIfAbsent(date, s -> Lists.newArrayList());
                symptomLastMonthData.computeIfAbsent(date, s -> Lists.newArrayList());
                pacsReports.computeIfAbsent(date, s -> Lists.newArrayList());
                childPacsReports.computeIfAbsent(date, s -> Lists.newArrayList());
            }
        });

        //查询最近30天
        DateRange lastMonDateTimes = new DateRange(lastMonDate, DateUtil.parse(req.getEndDate(), DatePattern.PURE_DATE_PATTERN), DateField.DAY_OF_MONTH);
        lastMonDateTimes.forEach(dateTime -> {
            List<MonMonitor> monitors = Lists.newArrayList();
            String date = dateTime.toString(DatePattern.PURE_DATE_PATTERN);

            //当前日期的数据，前十的疾病
            List<LyDiseaseTag> currentDisease = diseaseLastMonthData.get(date).stream().sorted(Comparator.comparing(LyDiseaseTag::getCountOfDiseases).reversed()).limit(10).collect(Collectors.toList());

            //七天前日期的数据,前十的疾病
            String offsetDate = dateTime.offset(DateField.DAY_OF_MONTH, -7).toString(DatePattern.PURE_DATE_PATTERN);
            List<LyDiseaseTag> offsetDisease = diseaseLastMonthData.get(offsetDate).stream().sorted(Comparator.comparing(LyDiseaseTag::getCountOfDiseases).reversed()).limit(10).collect(Collectors.toList());
            ;

            List<AlertLevelEnum> diseaseAlertLevel = Lists.newArrayList();
            for (LyDiseaseTag lyDiseaseTag : currentDisease) {
                //通过比对疾病名称，找到该疾病上个周期的人数
                LyDiseaseTag offsetLyDisease = offsetDisease.stream().filter(diseaseTag -> lyDiseaseTag.getDiseaseTag().equals(diseaseTag.getDiseaseTag())).collect(Collectors.toList()).stream().findFirst().orElse(null);

                //比较上期人数，是否是告警
                if (offsetLyDisease != null && offsetLyDisease.getCountOfDiseases() != 0) {
                    diseaseAlertLevel.add(alertLevel((double) lyDiseaseTag.getCountOfDiseases() / offsetLyDisease.getCountOfDiseases()));
                }
            }
            monitors.add(MonMonitor.builder()
                    .date(date)
                    .type("门、急诊疾病谱")
                    .level(AlertLevelUtil.calAlertLevel(diseaseAlertLevel).getLevel()).build());

            //-------------------------------------------------------------------------------------------------------------
            //当前日期的数据,前十的疾病
            List<LyDiseaseSymptom> currentSymptom = symptomLastMonthData.get(date).stream().sorted(Comparator.comparing(LyDiseaseSymptom::getCountOfSymptom).reversed()).limit(10).collect(Collectors.toList());
            //七天前日期的数据,前十的疾病
            List<LyDiseaseSymptom> offsetSymptoms = symptomLastMonthData.get(offsetDate).stream().sorted(Comparator.comparing(LyDiseaseSymptom::getCountOfSymptom).reversed()).limit(10).collect(Collectors.toList());

            List<AlertLevelEnum> symptomAlertLevel = Lists.newArrayList();
            for (LyDiseaseSymptom symptom : currentSymptom) {
                //通过比对症状名称，找到该症状上个周期的人数
                LyDiseaseSymptom offsetSymptom = offsetSymptoms.stream().filter(diseaseTag -> symptom.getSymptom().equals(diseaseTag.getSymptom())).collect(Collectors.toList()).stream().findFirst().orElse(null);

                //比较上期人数，是否是告警
                if (offsetSymptom != null && offsetSymptom.getCountOfSymptom() != 0) {
                    symptomAlertLevel.add(alertLevel((double) symptom.getCountOfSymptom() / offsetSymptom.getCountOfSymptom()));
                }
            }

            monitors.add(MonMonitor.builder()
                    .date(date)
                    .type("特殊症状、体征异常人次数占比")
                    .level(AlertLevelUtil.calAlertLevel(symptomAlertLevel).getLevel()).build());

            //-------------------------------------------------------------------------------------------------------------
            //当前日期的数据
            List<LyPacsReport> currentPacsReports = pacsReports.get(date);
            //七天前日期的数据
            List<LyPacsReport> offsetPacsReports = pacsReports.get(offsetDate);

            List<AlertLevelEnum> pacsReportAlertLevel = Lists.newArrayList();
            for (LyPacsReport currentlyPacsReport : currentPacsReports) {
                //通过比对，找到上个周期肺炎的人数
                LyPacsReport offsetPacsReport = offsetPacsReports.stream().filter(lyPacsReport -> currentlyPacsReport.getPneumonia() == lyPacsReport.getPneumonia()).collect(Collectors.toList()).stream().findFirst().orElse(null);

                //比较上期人数，是否是告警
                if (offsetPacsReport != null && offsetPacsReport.getCountOfPneumonia() != 0) {
                    pacsReportAlertLevel.add(alertLevel((double) currentlyPacsReport.getCountOfPneumonia() / offsetPacsReport.getCountOfPneumonia()));
                }
            }
            monitors.add(MonMonitor.builder()
                    .date(date)
                    .type("门、急诊特殊检查指标异常占比")
                    .level(AlertLevelUtil.calAlertLevel(pacsReportAlertLevel).getLevel()).build());

            //-------------------------------------------------------------------------------------------------------------
            //当前日期的数据
            List<LyPacsReport> currentChildPacsReports = childPacsReports.get(date);
            //七天前日期的数据
            List<LyPacsReport> offsetChildPacsReports = childPacsReports.get(offsetDate);

            List<AlertLevelEnum> pacsChildReportAlertLevel = Lists.newArrayList();
            for (LyPacsReport currentlyPacsReport : currentChildPacsReports) {
                //通过比对，找到上个周期肺炎的人数
                LyPacsReport offsetChildReport = offsetChildPacsReports.stream().filter(lyPacsReport -> currentlyPacsReport.getPneumonia() == lyPacsReport.getPneumonia()).collect(Collectors.toList()).stream().findFirst().orElse(null);

                //比较上期人数，是否是告警
                if (offsetChildReport != null && offsetChildReport.getCountOfPneumonia() != 0) {
                    pacsChildReportAlertLevel.add(alertLevel((double) currentlyPacsReport.getCountOfPneumonia() / offsetChildReport.getCountOfPneumonia()));
                }
            }
            monitors.add(MonMonitor.builder()
                    .date(date)
                    .type("14周岁以下儿童门、急诊特殊检查指标异常占比")
                    .level(AlertLevelUtil.calAlertLevel(pacsChildReportAlertLevel).getLevel()).build());

            lastMonMonitorMap.put(date, monitors);
        });

        return lastMonMonitorMap;
    }

    /**
     * 查询门急诊疾病
     *
     * @param req 时间
     * @return
     */
    private MonitorDiseaseAndPacsBarVO.DiseaseTag queryDisease(Req req, int count, MonitorDiseaseAndPacsBarVO monitorVO) {
        List<LyDiseaseTag> lyDiseaseTags = lyDiseaseTagDao.queryDiseaseByBetweenDate(req.getStartDate(), req.getEndDate());

        //疾病种类
        monitorVO.setDiseaseCategory(lyDiseaseTags.size());

        List<String> diseaseList = Lists.newArrayList();
        List<String> diseaseCount = Lists.newArrayList();

        //top10
        List<LyDiseaseTag> top10DiseaseList = lyDiseaseTags.stream().sorted(Comparator.comparing(LyDiseaseTag::getCountOfDiseases).reversed()).limit(10).collect(Collectors.toList());

        top10DiseaseList.forEach(lyDiseaseTag -> {
            diseaseList.add(lyDiseaseTag.getDiseaseTag());
            diseaseCount.add(StrUtil.format("{},{}",
                    lyDiseaseTag.getCountOfDiseases(), NumberUtil.formatPercent((double) lyDiseaseTag.getCountOfDiseases() / count, 2)));
        });
        return MonitorDiseaseAndPacsBarVO.DiseaseTag.builder().diseases(diseaseList).percents(diseaseCount).build();
    }

    /**
     * 特殊症状、体征异常人次数占比
     *
     * @param req 时间
     * @return
     */
    private MonitorDiseaseAndPacsBarVO.Symptom querySymptomPercent(Req req, int count) {
        List<LyDiseaseSymptom> result = lyDiseaseSymptomDao.querySymptomByDate(req.getStartDate(), req.getEndDate());

        List<String> symptomCategorys = Arrays.stream(SymptomCategoryEnum.values()).map(SymptomCategoryEnum::getDesc).collect(Collectors.toList());

        List<LyDiseaseSymptom> replenishList = symptomCategorys.stream().filter(symptom -> result.stream().noneMatch(lyDiseaseSymptom -> symptom.equals(lyDiseaseSymptom.getSymptom()))).map(s -> {
            LyDiseaseSymptom lyDiseaseSymptom = new LyDiseaseSymptom();
            lyDiseaseSymptom.setSymptom(s);
            lyDiseaseSymptom.setDay(req.getStartDate());
            lyDiseaseSymptom.setCountOfSymptom(0);
            return lyDiseaseSymptom;
        }).collect(Collectors.toList());
        //填充默认的体征进去
        result.addAll(replenishList);

        List<String> symptomList = Lists.newArrayList();
        List<String> percentList = Lists.newArrayList();

        result.forEach(lyDiseaseSymptom -> {
            symptomList.add(lyDiseaseSymptom.getSymptom());
            percentList.add(StrUtil.format("{},{}",
                    lyDiseaseSymptom.getCountOfSymptom(), count > 0 ? (NumberUtil.formatPercent((double) lyDiseaseSymptom.getCountOfSymptom() / count, 2)) : 0))
            ;
        });
        return MonitorDiseaseAndPacsBarVO.Symptom.builder().symptom(symptomList).percents(percentList).build();
    }

    /**
     * 门、急诊特殊检查指标异常占比 && 14周岁以下儿童门、急诊特殊检查指标异常占比
     *
     * @param req 时间
     * @return
     */
    private void queryAbnormalAndChild(Req req, MonitorDiseaseAndPacsBarVO monitorVO) {
        List<LyPacsReport> lyPacsReports = lyPacsReportDao.queryAllByDate(req.getStartDate(), req.getEndDate());

        if (CollUtil.isEmpty(lyPacsReports)) {
            return;
        }
        //阳性人数
        List<LyPacsReport> positiveList = lyPacsReports.stream().filter(lyPacsReport -> Constant.POSITIVE.equals(lyPacsReport.getNegativePositive())).collect(Collectors.toList());

        //肺炎集合
        List<LyPacsReport> pneumoniaList = lyPacsReports.stream().filter(lyPacsReport -> Constant.PNEUMONIA == lyPacsReport.getPneumonia()).collect(Collectors.toList());

        MonitorDiseaseAndPacsBarVO.AbnormalPercent abnormalPercent = MonitorDiseaseAndPacsBarVO.AbnormalPercent.builder()
                .totalCount(lyPacsReports.size())
                .abnormalCountAndPercent(StrUtil.format("{},{}", positiveList.size(), NumberUtil.formatPercent((double) positiveList.size() / lyPacsReports.size(), 2)))
                .pneumoniaCountAndPercent(StrUtil.format("{},{}", pneumoniaList.size(), NumberUtil.formatPercent((double) pneumoniaList.size() / lyPacsReports.size(), 2)))
                .build();

        //14岁以下儿童
        List<LyPacsReport> childList = lyPacsReportDao.queryChildAllByDate(req.getStartDate(), req.getEndDate());
        //阳性人数
        List<LyPacsReport> childPositiveList = childList.stream().filter(lyPacsReport -> Constant.POSITIVE.equals(lyPacsReport.getNegativePositive())).collect(Collectors.toList());

        //肺炎集合
        List<LyPacsReport> childPneumoniaList = childList.stream().filter(lyPacsReport -> Constant.PNEUMONIA == lyPacsReport.getPneumonia()).collect(Collectors.toList());

        MonitorDiseaseAndPacsBarVO.ChildPercent childPercent = MonitorDiseaseAndPacsBarVO.ChildPercent.builder()
                .totalCount(childList.size())
                .abnormalCountAndPercent(StrUtil.format("{},{}", childPositiveList.size(), NumberUtil.formatPercent((double) childPositiveList.size() / lyPacsReports.size(), 2)))
                .pneumoniaCountAndPercent(StrUtil.format("{},{}", childPneumoniaList.size(), NumberUtil.formatPercent((double) childPneumoniaList.size() / lyPacsReports.size(), 2)))
                .build();

        monitorVO.setChildPercent(childPercent);
        monitorVO.setAbnormalPercent(abnormalPercent);

        //检查总条数
        monitorVO.setExamCount(lyPacsReports.size());
        //儿童总条数
        monitorVO.setChildrenDiagnoseCount(childList.size());
    }

}